#!/bin/bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# shellcheck source=meta-google/recipes-google/ipmi/ipmi-fru-sh/lib.sh
source /usr/share/ipmi-fru/lib.sh || exit
source /usr/share/network/lib.sh  || exit

ipmi_fru_alloc '@EEPROM@' eeprom || exit

header=()
read_header "$eeprom" header || exit
internal_offset=${header[$IPMI_FRU_COMMON_HEADER_INTERNAL_OFFSET_IDX]}
if (( internal_offset == 0 )); then
  echo "Internal offset invalid for eeprom" >&2
  exit 1
fi

# Our MAC Address configuration lives in the internal area with a format
#   Offset Data
#        0 Version (Always 1)
#        1 Type (Always 1 for MAC Address)
#        2 Area Length in bytes (Always 32 bytes or 4 IPMI FRU sectors)
#      3-8 MAC Address Base Octets
#        9 Num Allocate MACs from Base
#    10-30 Padding (Always 0xFF)
#       31 IPMI FRU Checksum
internal=()
read_area "$eeprom" "$internal_offset" internal 4 || exit
if (( internal[1] != 1 || internal[2] != 32 )); then
  echo "Not a MAC internal region" >&2
  exit 1
fi
mac=("${internal[@]:3:6}")
num="${internal[9]}"
macstr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' "${mac[@]}")
echo "Base MAC $macstr num $num" >&2

rc=0

# Pre-Determine if we will miss an allocation due to the number of
# addresses the FRU actually supports.
# shellcheck disable=SC2190
declare -A num_to_intfs=(@NUM_TO_INTFS@)
for key in "${!num_to_intfs[@]}"; do
  if (( key >= num )); then
    echo "${num_to_intfs[$key]} at $key is out of range" >&2
    rc=1
  fi
done

# Write out each MAC override to the runtime networkd configuration
lower=$(((mac[3] << 16) | (mac[4] << 8) | mac[5]))
rm -f "/run/gbmc-mac-config/gbmc-mac-list"
mkdir -p "/run/gbmc-mac-config"
for (( i=0; i<num; i++ )); do
  if (( lower > 0xffffff )); then
    echo "MAC assignment too large: ${mac[*]}" >&2
    rc=2
    break
  fi
  mac[3]=$(((lower >> 16) & 0xff))
  mac[4]=$(((lower >> 8) & 0xff))
  mac[5]=$(((lower >> 0) & 0xff))
  macstr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' "${mac[@]}")

  echo "$macstr" >> "/run/gbmc-mac-config/gbmc-mac-list"
  for intf in ${num_to_intfs[$i]}; do
    echo "Setting $intf to $macstr" >&2
    for override in /run/systemd/network/{00,}-bmc-$intf.network.d; do
      mkdir -p "$override"
      printf '[Link]\nMACAddress=%s\n' "$macstr" >"$override"/50-mac.conf
      # Add ula for standard l2br
      if [[ "$intf" == "l2br" ]]; then
        eui64=$(mac_to_eui64 "$macstr")
        eui64="${eui64:2}"
        printf '[Address]\nAddress=fdb5:0481:10ce:1:%s/64\nPreferredLifetime=0\n' "$eui64" > "$override"/60-ula.conf
      fi
      # Add ula for gbmcbr
      if [[ "$intf" == "gbmcbr" ]]; then
        eui64=$(mac_to_eui64 "$macstr")
        eui64="${eui64:2}"
        printf '[Address]\nAddress=fdb5:0481:10ce:0:%s/64\nPreferredLifetime=0\n' "$eui64" > "$override"/60-ula.conf
      fi
    done
    for override in /run/systemd/network/{00,}-bmc-$intf.netdev.d; do
      mkdir -p "$override"
      printf '[NetDev]\nMACAddress=%s\n' "$macstr" >"$override"/50-mac.conf
    done
    # In case we don't have any interface configs, set the MAC directly
    # This is safe to apply, as systemd-networkd will always override this
    # value based on written configs.
    if ip link show "$intf" >/dev/null 2>&1 && \
       ! ip link set dev "$intf" address "$macstr"; then
      echo "Setting MAC($macstr) on $intf failed" >&2
    fi
  done
  (( ++lower ))
done

exit $rc
